﻿using System;
using System.Collections.Generic;
using System.Text;
using MathNet;
using FN = MathNet.Numerics.Fn;
using MathNet.Numerics.Distributions;

namespace HistogramPackage {
    // ===Generalized Beta===
    class GeneralizedBeta : PDF {
        public GeneralizedBeta() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 1 });
            greeks.Add(new Variable { type = Greek.BETA, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            greeks.Add(new Variable { type = Greek.TAU, value = 1 });
            lnFunction = "(LN((<X>/<THETA>)^(<ALPHA>*<TAU>)*(1-(<X>/<THETA>)^<TAU>)^(<BETA>-1)*<TAU>/<X>)+GAMMALN(<ALPHA>+<BETA>)-GAMMALN(<ALPHA>)-GAMMALN(<BETA>))*<FREQUENCY>";
            function = "(EXP(GAMMALN(<ALPHA>+<BETA>))/(EXP(GAMMALN(<ALPHA>))*EXP(GAMMALN(<BETA>)))*((<x>/<THETA>)^<TAU>)^<ALPHA>*(1-((<x>/<THETA>)^<TAU>))^(<BETA>-1)*<TAU>/<x>)";
            name = "Generalized Beta";
        }

        public override double getOutput(double x) {
            if (x >= theta) {
                throw new Exception("GeneralizedBeta: invalid range for x");
            }
            double u = Math.Pow(x / theta, tau);
            double numerator = FN.Gamma(alpha + beta) * Math.Pow(u, alpha) * Math.Pow(1 - u, beta - 1) * tau;
            double denominator = FN.Gamma(alpha) * FN.Gamma(beta) * x;
            return numerator / denominator;

        }

        public override double getMoment(double k) {
            return 0;
            //throw new Exception(this.ToString() + "\tgetMoment: not implemented");
        }

        // not in mathnet
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Beta===
    class Beta : PDF {

        BetaDistribution dist;

        public Beta() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 1 });
            greeks.Add(new Variable { type = Greek.BETA, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            dist = new BetaDistribution();
            lnFunction = "(LN((<X>/<THETA>)^<ALPHA>*(1-<X>/<THETA>)^(<BETA>-1)/<X>)+GAMMALN(<ALPHA>+<BETA>)-GAMMALN(<ALPHA>)-GAMMALN(<BETA>))*<FREQUENCY>";
            function = "((EXP(GAMMALN(<ALPHA>+<BETA>))/(EXP(GAMMALN(<ALPHA>)+GAMMALN(<BETA>))))*(((<x>/<THETA>)^<ALPHA>)*(1-(<x>/<THETA>))^(<BETA>-1))/<x>)";
            name = "Beta";
        }

        public override double getOutput(double x) {
            if (x >= theta) {
                throw new Exception("Beta: getOutput: Invalid X");
            }
            return 1;
        }

        public override double getMoment(double k) {
            return 0;
            //throw new Exception(this.ToString() + "\tgetMoment: not implemented");
        }

        // [IMPLEMENTED]
        public override double parametricVolume(double x) {
            dist.SetDistributionParameters(alpha, beta);
            return dist.CumulativeDistribution(x);
        }
    }

    // ===Generalized Pareto===
    class GeneralizedPareto : PDF {
        public GeneralizedPareto() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 1 });
            greeks.Add(new Variable { type = Greek.TAU, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            lnFunction = "(LN(<THETA>^<ALPHA>*<X>^(<TAU>-1)/(<X>+<THETA>)^(<ALPHA>+<TAU>))+GAMMALN(<ALPHA>+<TAU>)-GAMMALN(<ALPHA>)-GAMMALN(<TAU>))*<FREQUENCY>";
            function = "(EXP(GAMMALN(<ALPHA>+<TAU>))/EXP(GAMMALN(<ALPHA>)+GAMMALN(<TAU>))*(<THETA>^<ALPHA>)*<X>^(<TAU>-1)/(<X>+<THETA>)^(<ALPHA>+<TAU>))";
            name = "Generalized Pareto";
        }


        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double numerator = FN.Gamma(alpha + tau) * Math.Pow(theta, alpha) * Math.Pow(x, tau - 1);
            double denominator = FN.Gamma(alpha) * FN.Gamma(tau) * Math.Pow(x + theta, alpha + tau);
            return numerator / denominator;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (k <= -tau || k >= alpha) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            double numerator = Math.Pow(theta, k) * FN.Gamma(tau + k) * FN.Gamma(alpha - k);
            double denominator = FN.Gamma(alpha) * FN.Gamma(tau);
            return numerator / denominator;
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Transformed Beta===
    class TransformedBeta : PDF {
        public TransformedBeta() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 17.6050 });
            greeks.Add(new Variable { type = Greek.TAU, value = 12.1078 });
            greeks.Add(new Variable { type = Greek.GAMMA, value = 18.4313 });
            greeks.Add(new Variable { type = Greek.THETA, value = 27999.9908 });
            lnFunction = "(LN(<GAMMA>*(<X>/<THETA>)^(<GAMMA>*<TAU>)/(<X>*(1+(<X>/<THETA>)^<GAMMA>)^(<ALPHA>+<TAU>)))+GAMMALN(<ALPHA>+<TAU>)-GAMMALN(<ALPHA>)-GAMMALN(<TAU>))*<FREQUENCY>";
            function = "(EXP(GAMMALN(<ALPHA>+<TAU>))/EXP(GAMMALN(<ALPHA>)+GAMMALN(<TAU>)))*(<GAMMA>*(<x>/<THETA>)^(<GAMMA>*<TAU>))/(<x>*(1+(<x>/<THETA>)^<GAMMA>)^(<ALPHA>+<TAU>))";
            name = "Transformed Beta";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double numerator = FN.Gamma(alpha + tau) * gamma * Math.Pow(x / theta, gamma * tau);
            double denominator = FN.Gamma(alpha) * FN.Gamma(tau) * x * Math.Pow(1 + Math.Pow(x / theta, gamma), alpha + tau);
            return numerator / denominator;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (k <= -tau * gamma || k >= alpha * gamma) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            double numerator = Math.Pow(theta, k) * FN.Gamma(tau + k / gamma) * FN.Gamma(alpha - k / gamma);
            double denominator = FN.Gamma(alpha) * FN.Gamma(tau);
            return numerator / denominator;
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Inverse Burr===
    class InverseBurr : PDF {
        public InverseBurr() {
            greeks.Add(new Variable { type = Greek.TAU, value = 0.12356 });
            greeks.Add(new Variable { type = Greek.GAMMA, value = 12.26067 });
            greeks.Add(new Variable { type = Greek.THETA, value = 9.22701 });
            lnFunction = "(LN(<TAU> *<GAMMA>*(<X>/<THETA>)^(<GAMMA>*<TAU> )/(<X>*(1+(<X>/<THETA>)^<GAMMA>)^(<TAU>+1))))*<FREQUENCY>";
            function = "(<TAU>*<GAMMA>*(<x>/<THETA>)^(<GAMMA>*<TAU>))/(<x>*(1+(<x>/<THETA>)^<GAMMA>)^(<TAU>+1))*<FREQUENCY>";
            name = "Inverse Burr";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double numerator = tau * gamma * Math.Pow(x / theta, tau * gamma);
            double denominator = x * Math.Pow(1 + Math.Pow(x / theta, gamma), tau + 1);
            return numerator / denominator;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (k <= -tau * gamma || k >= gamma) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            double numerator = Math.Pow(gamma, k) * (tau + k / gamma) * FN.Gamma(1 - k / gamma);
            double denominator = FN.Gamma(tau);
            return numerator / denominator;
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===burr===
    class Burr : PDF {
        public Burr() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 118.27139 });
            greeks.Add(new Variable { type = Greek.TAU, value = 2 });
            greeks.Add(new Variable { type = Greek.GAMMA, value = 2.09957 });
            greeks.Add(new Variable { type = Greek.THETA, value = 62.99847 });
            lnFunction = "(LN(<ALPHA>*<GAMMA>*(<X>/<THETA>)^<GAMMA>/(<X>*(1+(<X>/<THETA>)^<GAMMA>)^(<ALPHA>+1))))*<FREQUENCY>";
            function = "(<ALPHA>*<GAMMA>*(<X>/<THETA>)^<TAU>)/(<X>*(1+(<X>/<THETA>)^<GAMMA>)^(<ALPHA>+1))*<FREQUENCY>";
            name = "Burr";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double numerator = alpha * gamma * Math.Pow(x / theta, tau);
            double denominator = x * Math.Pow(1 + Math.Pow(x / theta, gamma), alpha + 1);
            return numerator / denominator;
        }

        public override double getMoment(double k) {
            return 0;
            //throw new Exception(this.ToString() + "\tgetMoment: not implemented");
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Pareto===
    class Pareto : PDF {
        ParetoDistribution dist;

        public Pareto() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            dist = new ParetoDistribution();
            lnFunction = "(LN(<ALPHA>*<THETA>^<ALPHA>/(<X>+<THETA>)^(<ALPHA>+1)))*<FREQUENCY>";
            function = "(<ALPHA>*<THETA>^<ALPHA>)/(<X>+<THETA>)^(<ALPHA>+1)*<FREQUENCY>";
            name = "Pareto";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double numerator = alpha * Math.Pow(theta, alpha);
            double denominator = Math.Pow(x + theta, alpha + 1);
            return numerator / denominator;
        }

        public override double getMoment(double k) {
            return 0;
            //throw new Exception(this.ToString() + "\tgetMoment: not implemented");
        }

        // IN MATHNET, BUT p DON'T UNDERSTAND THE PARAMETERS
        public override double parametricVolume(double x) {
            // dist.SetDistributionParameters(); location and shape?
            return -1;
        }
    }

    //===Inverse Paralogistic===
    class InverseParalogistic : PDF {
        public InverseParalogistic() {
            greeks.Add(new Variable { type = Greek.TAU, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            lnFunction = "(LN((<TAU>^2)*(<X>/<THETA>)^(<TAU>^2)/(<X>*(1+(<X>/<THETA>)^<TAU>)^(<TAU>+1))))*<FREQUENCY>";
            function = "((<TAU>^2)*(<X>/<THETA>)^(<TAU>^2))/(<X>*(1+(<X>/<THETA>)^<TAU>)^(<TAU>+1))*<FREQUENCY>";
            name = "Inverse Paralogistic";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double numerator = Math.Pow(tau, 2) * Math.Pow(x / theta, Math.Pow(tau, 2));
            double denominator = x * Math.Pow(1 + Math.Pow(x / theta, tau), tau + 1);
            return numerator / denominator;
        }

        public override double getMoment(double k) {
            return 0;
            //throw new Exception(this.ToString() + "\tgetMoment: not implemented");
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Gamma===
    public class Gamma : PDF {
        GammaDistribution gammaDist;

        public Gamma() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            gammaDist = new GammaDistribution();
            lnFunction = "(LN((<X>/<THETA>)^<ALPHA>/(<X>*EXP(GAMMALN(<ALPHA>))))-<X>/<THETA>)*<FREQUENCY>";
            function = "((<X>/<THETA>)^<ALPHA>)*EXP(-<X>/<THETA>)/(<X>*EXP(GAMMALN(<ALPHA>)))*<FREQUENCY>";
            name = "Gamma";

        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double numerator = Math.Pow(x / theta, alpha) * Math.Exp(-x / theta);
            double denominator = x * FN.Gamma(alpha);
            return numerator / denominator;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (k <= -alpha) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            double numerator = Math.Pow(theta, k) * FN.Gamma(alpha + k);
            double denominator = FN.Gamma(alpha);
            return numerator / denominator;
        }

        // [IMPLEMENTED]
        public override double parametricVolume(double x) {
            gammaDist.Alpha = alpha;
            gammaDist.Theta = theta;
            return gammaDist.CumulativeDistribution(x);
        }
    }

    //===Inverse Transformed Gamma===
    public class InverseTransformedGamma : PDF {
        public InverseTransformedGamma() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 5.6620 });
            greeks.Add(new Variable { type = Greek.TAU, value = 18.0616 });
            greeks.Add(new Variable { type = Greek.THETA, value = 30000.0136 });
            lnFunction = "(LN(<TAU>*(<THETA>/<X>)^(<ALPHA>*<TAU>)/(<X>*EXP(GAMMALN(<ALPHA>))))-(<THETA>/<X>)^<TAU>)*<FREQUENCY>";
            function = "(<TAU>*((<THETA>/<X>)^(<ALPHA>*<TAU>))*EXP(-((<THETA>/<x>)^<TAU>)))/(<x>*EXP(GAMMALN(<ALPHA>)))*<FREQUENCY>";
            name = "Inverse Transformed Gamma";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double u = Math.Pow(theta / x, tau);
            double numerator = tau * Math.Pow(u, alpha) * Math.Exp(-u);
            double denominator = x * FN.Gamma(alpha);
            return numerator / denominator;

        }

        // implemented
        public override double getMoment(double k) {
            if (k >= alpha * tau) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            double numerator = Math.Pow(theta, k) * FN.Gamma(alpha - k / tau);
            double denomerator = FN.Gamma(alpha);
            return numerator / denomerator;
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            // alpha * gamma(x) ?
            return -1;
        }
    }

    // ===Transformed Gamma===
    public class TransformedGamma : PDF {
        public TransformedGamma() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 127.0642 });
            greeks.Add(new Variable { type = Greek.TAU, value = 1.1983 });
            greeks.Add(new Variable { type = Greek.THETA, value = 480.5062 });
            lnFunction = "(LN(<TAU>*((<X>/<THETA>)^(<ALPHA>*<TAU>))/(<X>*EXP(GAMMALN(<ALPHA>))))-(<X>/<THETA>)^<TAU>)*<FREQUENCY>";
            function = "(<TAU>*((<X>/<THETA>)^(<ALPHA>*<TAU>))*EXP(-((<X>/<THETA>)^<TAU>))/(<X>*EXP(GAMMALN(<ALPHA>))))*<FREQUENCY>";
            name = "Transformed Gamma";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double u = Math.Pow((x / theta), tau);
            double numerator = tau * Math.Pow(u, alpha) * Math.Exp(-u);
            double denominator = x * FN.Gamma(alpha);
            return numerator / denominator;
        }

        // implemented
        public override double getMoment(double k) {
            if (k <= alpha * tau) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            double numerator = Math.Pow(theta, k) * FN.Gamma((alpha + k) / tau);
            double denomerator = FN.Gamma(alpha);
            return numerator / denomerator;
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    // ===Single Parameter Pareto===
    public class SingleParameterPareto : PDF {
        public SingleParameterPareto() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            lnFunction = "(LN(<ALPHA>*<THETA>^<ALPHA>/<X>^(<ALPHA>+1)))*<FREQUENCY>";
            function = "(<ALPHA>*<THETA>^<ALPHA>)/<x>^(<ALPHA>+1)*<FREQUENCY>";
            name = "Single Parameter Pareto";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            if (x <= theta) {
                return 0;
                //throw new Exception("invalid input: SingleParameterPareto");
            }
            double num = alpha * Math.Pow(theta, alpha);
            double denom = Math.Pow(x, alpha + 1);
            return num / denom;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (k >= alpha) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            double numerator = alpha * Math.Pow(theta, k);
            double denomerator = alpha - k;
            return numerator / denomerator;
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Inverse Gaussian===
    public class InverseGaussian : PDF {
        public InverseGaussian() {
            greeks.Add(new Variable { type = Greek.MU, value = 27489.1261 });
            greeks.Add(new Variable { type = Greek.THETA, value = 61340922.1369 });
            lnFunction = "(LN(<THETA>/(2*PI()*<X>^3))-<THETA>*((<X>-<MU>)/<MU>)^2/(2*<X>))*<FREQUENCY>";
            function = "(<THETA>/(2*PI()*<X>^3))*EXP(-(<THETA>*((<X>-<MU>)/<MU>)^2)/(2*<X>))*<FREQUENCY>";
            name = "Inverse Gaussian";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double z = (x - mu) / mu;
            double a = Math.Pow(theta / (2 * Math.PI * Math.Pow(x, 3)), 1 / 2);
            double b = Math.Exp(-theta * Math.Pow(z, 2) / (2 * x));
            return a * b;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            return mu; // documentation only specifies the first moment
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Lognormal===
    public class Lognormal : PDF {
        LognormalDistribution logDist;

        public Lognormal() {
            greeks.Add(new Variable { type = Greek.MU, value = 10.2317 });
            greeks.Add(new Variable { type = Greek.SIGMA, value = .0243 });
            logDist = new LognormalDistribution();
            lnFunction = "(LN((1/(<X>*<SIGMA>*SQRT(2*PI()))))-(LN(<X>)-<MU>/<SIGMA>)^2/2)*<FREQUENCY>";
            function = "(1/(<X>*<SIGMA>*SQRT(2*PI())))*EXP(-(((LOG(<X>)-<MU>)/<SIGMA>)^2)/2)*<FREQUENCY>";
            name = "Lognormal";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            if (x == 0) {
                return 0;
            }
            double test1 = Math.Log(x);
            double test2 = mu;
            double test3 = sigma;


            double z = (Math.Log(x) - mu) / sigma;
            double num = Math.Exp(-Math.Pow(z, 2) / 2);
            double denom = x * sigma * Math.Sqrt(2 * Math.PI);
            return num / denom;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            return Math.Exp(k * mu + Math.Pow(k, 2) * Math.Pow(sigma, 2) / 2);
        }

        // [IMPLEMENTED]
        public override double parametricVolume(double x) {
            logDist.SetDistributionParameters(mu, sigma);
            double t1 = logDist.CumulativeDistribution(x);
            return t1;
        }
    }

    //===Inverse Exponential===
    public class InverseExponential : PDF {
        public InverseExponential() {
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            lnFunction = "(LN(<THETA>/<X>^2)-<THETA>/<X>)*<FREQUENCY>";
            function = "(<THETA>*EXP(-<THETA>/<X>))/(<X>^2)*<FREQUENCY>";
            name = "Inverse Exponential";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double numerator = theta * Math.Exp(-theta / x);
            double denominator = Math.Pow(x, 2);
            return numerator / denominator;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (k >= 1) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            return Math.Pow(theta, k) * FN.Gamma(1 - k);
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Exponential===
    public class Exponential : PDF {
        ExponentialDistribution dist;

        public Exponential() {
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            dist = new ExponentialDistribution();
            lnFunction = "(LN(1/<THETA>)-<X>/<THETA>)*<FREQUENCY>";
            function = "(EXP(-<X>/<THETA>)/<THETA>)*<FREQUENCY>";
            name = "Exponential";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            return Math.Exp(-x / theta) / theta;
        }

        // partially implemented
        public override double getMoment(double k) {
            if (k > -1) {
                return Math.Pow(theta, k) * FN.Gamma(k + 1);
            }
            // if k is an integer:

            return -1;
        }

        // [IMPLEMENTED]
        public override double parametricVolume(double x) {
            dist.SetDistributionParameters(theta);//rate vs theta in argument
            double a1 = dist.CumulativeDistribution(x);
            double a2 = 1 - Math.Exp(-theta * x);
            return a2;
        }
    }

    //===Inverse Weibull==
    public class InverseWeibull : PDF {
        public InverseWeibull() {
            greeks.Add(new Variable { type = Greek.TAU, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            lnFunction = "(LN(<TAU>*(<THETA>/<X>)^<TAU>/<X>)-((<THETA>/<X>)^<TAU>))*<FREQUENCY>";
            function = "(<TAU>*((<THETA>/<X>)^<TAU>)*EXP(-((<THETA>/<X>)^<TAU>))/<X>)*<FREQUENCY>";
            name = "Inverse Weibull";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double num = tau * Math.Pow(theta / x, tau) * Math.Exp(Math.Pow(theta / x, tau));
            double denom = x;
            return num / denom;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (k >= tau) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }

            return Math.Pow(theta, k) * FN.Gamma(1 - k / tau);
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Weibull===
    public class Weibull : PDF {
        public Weibull() {
            greeks.Add(new Variable { type = Greek.TAU, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            lnFunction = "(LN(<TAU>*((<X>/<THETA>)^<TAU>)/<X>)-((<X>/<THETA>)^<TAU>))*<FREQUENCY>";
            function = "(<TAU>*((<X>/<THETA>)^<TAU>)*EXP(-((<X>/<THETA>)^<TAU>))/<X>)*<FREQUENCY>";
            name = "Weibull";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double num = tau * Math.Pow(x / theta, tau) * Math.Exp(Math.Pow(x / theta, tau));
            double denom = x;
            return num / denom;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (k <= -tau) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            return Math.Pow(theta, k) * FN.Gamma(1 + k / tau);
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Inverse Gamma===
    public class InverseGamma : PDF {
        public InverseGamma() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            lnFunction = "(LN((<THETA>/<X>)^<ALPHA>/(<X>*EXP(GAMMALN(<ALPHA>))))-<THETA>/<X>)*<FREQUENCY>";
            function = "((<THETA>/<X>)^<ALPHA>)*EXP(-<THETA>/<X>)/(<X>*EXP(GAMMALN(<ALPHA>)))*<FREQUENCY>";
            name = "Inverse Gamma";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double num = Math.Pow(theta / x, alpha) * Math.Exp(-theta / x);
            double denom = x * FN.Gamma(alpha);
            return num / denom;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (k >= alpha) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            double numerator = Math.Pow(theta, k) * FN.Gamma(alpha - k);
            double denomerator = FN.Gamma(alpha);
            return numerator / denomerator;
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Paralogistic===
    public class Paralogistic : PDF {
        public Paralogistic() {
            greeks.Add(new Variable { type = Greek.ALPHA, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            lnFunction = "(LN((<ALPHA>^2)*(<X>/<THETA>)^<ALPHA>/(<X>*(1+(<X>/<THETA>)^<ALPHA>)^(<ALPHA>+1))))*<FREQUENCY>";
            function = "((<ALPHA>^2)*(<x>/<THETA>)^<ALPHA>)/(<x>*(1+(<x>/<THETA>)^<ALPHA>)^(<ALPHA>+1))*<FREQUENCY>";
            name = "Paralogistic";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double num = Math.Pow(alpha, 2) * Math.Pow(x / theta, alpha);
            double denom = x * Math.Pow(1 + Math.Pow(x / theta, alpha), alpha + 1);
            return num / denom;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (k <= -alpha || k >= Math.Pow(alpha, 2)) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }

            double numerator = Math.Pow(theta, k) * (1 + k / alpha) * FN.Gamma(alpha - k / alpha);
            double denomerator = FN.Gamma(alpha);
            return numerator / denomerator;
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Logligistic===
    public class Loglogistic : PDF {
        public Loglogistic() {
            greeks.Add(new Variable { type = Greek.GAMMA, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            lnFunction = "(LN(<GAMMA>*(<X>/<THETA>)^<GAMMA>/(<X>*(1+(<X>/<THETA>)^<GAMMA>)^2)))*<FREQUENCY>";
            function = "(<GAMMA>*((<X>/<THETA>)^<GAMMA>)/(<X>*(1+(<X>/<THETA>)^<GAMMA>)^2))*<FREQUENCY>";
            name = "Loglogistic";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double num = gamma * Math.Pow(x / theta, gamma);
            double denom = x * Math.Pow(1 + Math.Pow(x / theta, gamma), 2);
            return num / denom;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (k <= -gamma || k >= gamma) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            return Math.Pow(theta, k) * FN.Gamma(1 + k / gamma) * FN.Gamma(1 - k / gamma);
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }

    //===Inverse Pareto===
    public class InversePareto : PDF {
        public InversePareto() {
            greeks.Add(new Variable { type = Greek.TAU, value = 1 });
            greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            lnFunction = "(LN(<TAU>*<THETA>*<X>^(<TAU>-1)/((<X>+<THETA>)^(<TAU>+1))))*<FREQUENCY>";
            function = "(<TAU>*<THETA>*(<X>^(<TAU> - 1))/((<X>+<THETA>)^(<TAU>+1)))*<FREQUENCY>";
            name = "Inverse Pareto";
        }

        // [IMPLEMENTED]
        public override double getOutput(double x) {
            double num = tau * theta * Math.Pow(x, tau - 1);
            double denom = Math.Pow(x + theta, tau + 1);
            return num / denom;
        }

        // [IMPLEMENTED]
        public override double getMoment(double k) {
            if (-tau >= k || k >= 1) {
                return 0;
                //throw new Exception(this.ToString() + "\tgetMoment: invalid k");
            }
            double numerator = Math.Pow(theta, k) * FN.Gamma(tau + k) * FN.Gamma(1 - k);
            double denomerator = FN.Gamma(tau);
            return numerator / denomerator;
        }

        // NOT IN MATHNET
        public override double parametricVolume(double x) {
            return -1;
        }
    }
}
